/**    
 * 文件名：JavaCvMatch.java    
 *    
 * 版本信息：    
 * 日期：2014年3月13日    
 * xyj 足下 xyj 2014     
 * 版权所有    
 *    
 */

import static com.googlecode.javacv.cpp.opencv_highgui.CV_LOAD_IMAGE_GRAYSCALE;
import static com.googlecode.javacv.cpp.opencv_imgproc.cvResize;
import static opencvtest.OpenCVUtils.loadAndShowOrExit;
import static opencvtest.OpenCVUtils.show;

import java.io.File;

import com.googlecode.javacv.cpp.opencv_core.CvMat;
import com.googlecode.javacv.cpp.opencv_core.CvSize;
import com.googlecode.javacv.cpp.opencv_core.IplImage;

/**
 * @项目名称：opencv-test
 * @类名称：JavaCvMatch
 * @类描述：
 * @创建人：zhuyi
 * @创建时间：2014年3月13日 上午9:47:12
 * @修改人：zhuyi
 * @修改时间：2014年3月13日 上午9:47:12
 * @修改备注：
 * @version
 * 
 */
public class JavaCvMatch {

    public static void main(String[] args) {

        IplImage image = loadAndShowOrExit(new File("E:/image-test/b1.jpg"), CV_LOAD_IMAGE_GRAYSCALE);
        IplImage destImage = IplImage.create(new CvSize(8, 8), image.depth(), image.nChannels());
        cvResize(image, destImage);
        colorReduce(destImage);
        mask(destImage, mean(destImage));
        show(destImage, "resize image");

        IplImage image2 = loadAndShowOrExit(new File("E:/image-test/b2.jpg"), CV_LOAD_IMAGE_GRAYSCALE);
        IplImage destImage2 = IplImage.create(new CvSize(8, 8), image2.depth(), image2.nChannels());
        cvResize(image2, destImage2);
        colorReduce(destImage2);
        mask(destImage2, mean(destImage2));
        show(destImage2, "resize image2");

        String hash = hash(destImage);
        String hash2 = hash(destImage2);

        System.out.println(hash);
        System.out.println(hash2);

        System.out.println(hanmingDistance(hash, hash2));
    }

    // 汉明距离计算
    public static int hanmingDistance(String str1, String str2) {
        if ((str1.length() != 64) || (str2.length() != 64))
            return -1;
        int difference = 0;
        for (int i = 0; i < 64; i++) {
            if (!str1.substring(i, i + 1).equals(str2.substring(i, i + 1))) {
                difference++;
            }
        }
        return difference;
    }

    public static String hash(IplImage image) {

        CvMat mat = image.asCvMat();
        StringBuilder sb = new StringBuilder();
        int nbElements = mat.rows() * mat.cols() * mat.channels();
        for (int i = 0; i < nbElements; i++) {
            int v = (int) mat.get(i);
            if (v == 0) {
                sb.append("0");
            } else {
                sb.append("1");
            }
        }

        return sb.toString();
    }

    public static void mask(IplImage image, int average) {

        CvMat mat = image.asCvMat();
        int nbElements = mat.rows() * mat.cols() * mat.channels();
        for (int i = 0; i < nbElements; i++) {
            int v = (int) mat.get(i);
            if (v > average) {
                mat.put(i, 1);
            } else {
                mat.put(i, 0);
            }
        }
    }

    public static int mean(IplImage image) {

        CvMat mat = image.asCvMat();
        int nbElements = mat.rows() * mat.cols() * mat.channels();
        int count = 0;
        for (int i = 0; i < nbElements; i++) {
            int v = (int) mat.get(i);
            count = count + v;
        }

        return count / nbElements;

    }

    /**
     * Reduce number of colors, described in OpenCV Cookbook Chapter 2.
     * 
     * @param image
     *            input image that will have colors modified after this call.
     * @param div
     *            color reduction factor.
     */
    public static void colorReduce(IplImage image) {

        CvMat mat = image.asCvMat();

        // Total number of elements, combining components from each channel
        int nbElements = mat.rows() * mat.cols() * mat.channels();
        for (int i = 0; i < nbElements; i++) {
            // Convert to integer
            int v = (int) mat.get(i);
            // Use integer division to reduce number of values
            int newV = v / 4;
            // Put back into the image
            mat.put(i, newV);
        }

    }
}
